# coding:utf-8
import pandas as pd
import random
import shutil
import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
import seaborn as sns
import matplotlib.pyplot as plt
import os.path
import datetime
import numpy as np
from .models import UserLog
from . import config


# 算法
class Algorithm:
    # 用户
    user_id = ""
    # 算法名称,例如"K均值"
    algorithm_name = ""
    # 实例ID,全局唯一,例如"20180718155027_983003"
    instance_id = ""
    # 数据源名称,例如"iris.xlsx"
    data_source_name = ""
    # 数据源文件绝对路径,例如"D:\jupyter-workspace\20180718155027_983003\iris.xlsx"
    data_source_file_path = ""
    # 数据源(DataFrame对象)
    data_source = None
    # 数据源记录行数
    data_source_size = 0
    # 输入字段列表
    input_field_names = []
    # 总样本(输入)
    inputs = []
    # 工作目录
    work_folder_path = ""
    # ipynb模板文件名
    ipynb_template_name = ""
    # ipynb目标文件路径
    ipynb_dest_path = ""
    # ipynb模板替换清单
    ipynb_items = {}
    # 统计图表目录
    chart_path = ""
    # 结果excel文件路径
    result_excel_path = ""
    # 算法实例
    algorithm = None
    # 是否要生成字段统计性描述图片
    will_create_field_desc_chart = True;
    
    def __init__(self):
        # 类构造方法
        # 初始化实例ID("日期时间_随机数字")
        self.instance_id = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random.randrange(1, 1000000, 1))
        # 创建工作目录
        self.work_folder_path = os.path.join(config.jupyter_workspace_path, self.instance_id)
        os.makedirs(self.work_folder_path)
        # 创建图表目录
        self.chart_path = os.path.join(self.work_folder_path, "chart")
        os.makedirs(self.chart_path)
        # ipynb目标文件路径
        self.ipynb_dest_path = os.path.join(self.work_folder_path, "source_code.ipynb")
        # 结果excel文件路径
        self.result_excel_path = os.path.join(self.work_folder_path, "result.xlsx")
        # 线程安全
        plt.switch_backend('agg')
        # 统一设置matplotlib字体,防止中文出现乱码
        plt.rcParams["font.sans-serif"] = ["SimHei"]
        plt.rcParams['axes.unicode_minus'] = False
        np.random.seed(1986)
        
    def readDataSouce(self, upload_file_path=""):
        # 读取数据源
        # 准备数据源文件
        if upload_file_path:
            # 手工上传
            self.data_source_name = "data_source.xlsx"
            self.data_source_file_path = os.path.join(self.work_folder_path, self.data_source_name)
            # 移动
            shutil.move(upload_file_path, self.data_source_file_path)
        else:
            # 本地模板
            self.data_source_file_path = os.path.join(self.work_folder_path, self.data_source_name)
            # 拷贝
            shutil.copyfile(os.path.join(config.sample_folder_path, self.data_source_name), self.data_source_file_path)
        
        # 读取
        if os.path.splitext(self.data_source_file_path)[1] == ".csv":
            # csv
            self.data_source = pd.read_csv(self.data_source_file_path)
        else:
            # excel
            self.data_source = pd.read_excel(self.data_source_file_path)
            
        # 记录行数
        self.data_source_size = self.data_source.iloc[:, 0].size
        
        # 生成字段描述图片(直方图)
        if self.will_create_field_desc_chart:
            _index = 1
            for field_name in self.data_source.columns.values:
                fig = plt.figure()
                ax = fig.add_subplot(111)
                ax = sns.countplot(data=self.data_source, x=field_name, ax=ax);
                if self.data_source[field_name].value_counts().size > 10:
                    # 若字段值种类过多,需要调整横轴坐标显示
                    ax.xaxis.set_major_locator(plt.AutoLocator())
                ax.set_title(field_name)
                ax.set_xlabel("属性值")
                ax.set_ylabel("频数")
                fig.savefig("%s/%d.png" % (self.chart_path, _index))
                _index = _index + 1
        
    def setInPutFieldName(self, input_field_names):
        # 区分输入
        self.input_field_names = input_field_names
        self.inputs = self.data_source[self.input_field_names].values
        
    def implent(self):
        # 执行算法
        log = UserLog()
        log.user_id = self.user_id
        log.log_content = self.algorithm_name
        log.save()
    
    def saveToExcle(self):
        # 保存结果excel文件
        self.data_source.to_excel(self.result_excel_path)
        
    def drawChart(self):
        # 保存结果图表
        pass;
    
    def prepareIpynbItems(self):
        # 初始化ipynb_items清单项
        self.ipynb_items = {}
        # jupyter无法识别"\",需要转换为"/"
        self.ipynb_items["#data_source#"] = self.data_source_file_path.replace("\\", "/")
        self.ipynb_items["#input_field_names#"] = self.input_field_names
    
    def generateNotebookFile(self):
        # 生成ipynb目标文件
        # 读取ipynb模板文件,替换并执行
        self.prepareIpynbItems()
        with open(os.path.join(config.sample_folder_path, self.ipynb_template_name), encoding="UTF-8") as f1:
            nb = nbformat.read(f1, as_version=4)
            # 根据ipynb_items,替换模板
            for cell in nb["cells"]:
                if cell["cell_type"] == "code":
                    for key in self.ipynb_items:
                        cell["source"] = cell["source"].replace(str(key), str(self.ipynb_items[key]))
            # 执行(耗时)
            ep = ExecutePreprocessor(timeout=1000, kernel_name="python3")
            ep.preprocess(nb, {"metadata":{}})
        
        # 保存至目标ipynb文件
        with open(self.ipynb_dest_path, "wt", encoding="UTF-8") as f2:
            nbformat.write(nb, f2)
